import MagicString from 'magic-string'
import rollup from 'rollup'
import esbuild from 'esbuild'

export const ignoreDynamicRequires = []

export function definePlugin(
  userDefine: Record<string, any>,
  { mode }: { mode: string },
  sourcemap?: boolean
): rollup.Plugin {
  const processNodeEnv: Record<string, string> = {
    'process.env.NODE_ENV': JSON.stringify(mode),
    'global.process.env.NODE_ENV': JSON.stringify(mode),
    'globalThis.process.env.NODE_ENV': JSON.stringify(mode),
  }

  for (const key in userDefine) {
    const val = userDefine[key]
    userDefine[key] = typeof val === 'string' ? val : JSON.stringify(val)
  }

  const importMetaKeys: Record<string, string> = {
    'import.meta.env.': 'process.env.',
    'import.meta.env': 'process.env',
  }

  const generatePattern = (): [Record<string, string | undefined>, RegExp] => {
    const replacements: Record<string, string> = {
      ...processNodeEnv,
      ...userDefine,
      ...importMetaKeys,
    }

    const pattern = new RegExp(
      // Do not allow preceding '.', but do allow preceding '...' for spread operations
      '(?<!(?<!\\.\\.)\\.)\\b(' +
        Object.keys(replacements)
          .map((str) => {
            return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')
          })
          .join('|') +
        ')\\b',
      'g'
    )

    return [replacements, pattern]
  }

  const defaultPattern = generatePattern()

  return {
    name: 'electron:define',
    renderStart(options) {
      sourcemap = !!options.sourcemap
    },
    transform(code) {
      const [replacements, pattern] = defaultPattern

      const s = new MagicString(code)
      let hasReplaced = false
      let match: RegExpExecArray | null

      while ((match = pattern.exec(code))) {
        hasReplaced = true
        const start = match.index
        const end = start + match[0].length
        const replacement = '' + replacements[match[1]]
        s.overwrite(start, end, replacement)
      }

      if (!hasReplaced) {
        return null
      }

      return { code: s.toString(), map: sourcemap ? s.generateMap({ hires: true }) : undefined }
    },
  }
}

export function esbuildMinify(options: import('esbuild').TransformOptions = {}): rollup.Plugin {
  const { minify, minifyWhitespace, minifyIdentifiers, minifySyntax } = options

  const minifyOptions: import('esbuild').TransformOptions = {
    sourcemap: true,
    minify,
    minifyWhitespace,
    minifyIdentifiers,
    minifySyntax,
  }
  return {
    name: 'esbuild:minify',
    renderChunk(code) {
      return esbuild.transform(code, minifyOptions)
    },
  }
}
